/**********************************************************
** File Name:client.c
*  Author:Wangmeng
*  Data:2010.06.01
**********************************************************/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<limits.h>
#include<string.h>
#include<ctype.h>
#include<sys/types.h>
//#include<sys/socket.h>

//#include<sys/stat.h>
#include<netdb.h>
//#include<netinet/in.h>
//#include<arpa/inet.h>
#include<errno.h>
#include<dirent.h>
#include<sockets.h>
#include<dfs_posix.h>
#include <rtthread.h>
#define MAX_INPUT_SIZE 254
#define SERVER_IP "103.143.72.38"

#define SERVER_PORT 21
#define DATA_PORT 2048
#define FILEBUF_SIZE 1024
#define TYPE_I 0
#define TYPE_A 1

int TYPE = 0;
const char *MODE[] = {"BINARY", "ASCII"}; //模式选择
char buffer[FILEBUF_SIZE]; //定义文件缓存
char line_in[MAX_INPUT_SIZE + 1];
char *server_ip, *local_ip; //定义本地ip和服务器ip
char port_addr[24] = "";
int server_port, local_port;//定义服务器端口和本地端口
int server_sock, client_sock = -1, data_sock;
int z = 0;
struct sockaddr_in server_addr, client_addr, data_addr;
struct hostent *server_host;

#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       (1024*5)
#define THREAD_TIMESLICE        5
/*
Search through str looking for what. Replace any what with
the contents of by. Do not let the string get longer than max_length.
*/
int replace(char *str, char *what, char *by, int max_length);

//send msg to server
void send_msg(char *command, char *msg, int flag);

//some functions
void help_info();
void user_login();
void command_quit();
void command_syst();
void command_type();
void command_pwd();
void command_cd();
int command_port();
int data_conn(char *ip, int port);
void command_list();
void command_get(char *filename);
void command_put(char *filename);
int ftpSession(int argv, char **argc);

static rt_thread_t tid1 = RT_NULL;

static void thread1_entry(void *parameter) {
	ftpSession(1, 0);

	rt_thread_mdelay(1000);
}

void ftpthread() {
	tid1 = rt_thread_create("thread1",
	                        thread1_entry, RT_NULL,
	                        THREAD_STACK_SIZE,
	                        THREAD_PRIORITY, THREAD_TIMESLICE);
	if (tid1 != RT_NULL)
		rt_thread_startup(tid1);
}

//ftp会话控制
int ftpSession(int argv, char **argc) {
	if (argv == 1) {
		server_ip = SERVER_IP;
		server_port = SERVER_PORT;
	} else if (argv == 2) {
		server_ip = argc[1];
		server_port = SERVER_PORT;
	} else if (argv == 3) {
		server_ip = argc[1];
		server_port = atoi(argc[2]);
	}
	
	//connect to the ftp server
	server_host = lwip_gethostbyname(server_ip);
	if (server_host == (struct hostent *)NULL) {
		rt_kprintf(">gethostbyname failed\n");
		return (-1);
	}
	
	//setup the port for the connection
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	//memcpy(&server_addr.sin_addr, server_host->h_addr, server_host->h_length);
	server_addr.sin_addr = *((struct in_addr *)server_host->h_addr);
	server_addr.sin_port = htons(server_port);
	rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
	
	//get the socket
	server_sock = lwip_socket(PF_INET, SOCK_STREAM, 0);
	if (server_sock < 0) {
		rt_kprintf(">error on socket()\n");
		return (-1);
	}
	rt_thread_mdelay(500);

	//connect to the server
	if (lwip_connect(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
		rt_kprintf(">error on connect()\n");
		lwip_close(server_sock);
		return (-1);
	}

	//connect to the ftp server successful
	rt_kprintf(">Connected to %s:%d\n", server_ip, server_port);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
	//printf("z = %d, buffer is '%s'\n",z,buffer);
	//login
	user_login();
	command_pwd();
	command_list();
	command_get("ScanPort.exe");
	command_put("ScanPort.exe");
#if 0
	while (1) {
		//rt_kprintf(">");
		//fgets(line_in, MAX_INPUT_SIZE, stdin);
		//line_in[strlen(line_in)-1] = '\0';
		if (strncmp("quit", line_in, 4) == 0) {
			command_quit();
			break;
		} else if ((strncmp("?", line_in, 1) == 0) || (strncmp("help", line_in, 4) == 0)) {
			help_info();
		} else if (strncmp("syst", line_in, 4) == 0) {
			command_syst();
		} else if (strncmp("type", line_in, 4) == 0) {
			command_type();
		} else if (strncmp("pwd", line_in, 3) == 0) {
			command_pwd();
		} else if (strncmp("cd", line_in, 2) == 0) {
			command_cd();
		} else if (strncmp("port", line_in, 4) == 0) {
			command_port();
		} else if ((strncmp("ls", line_in, 4) == 0) || (strncmp("dir", line_in, 3) == 0)) {
			command_list();
		} else if (strncmp("get", line_in, 3) == 0) {
			command_get(&line_in[4]);
		} else if (strncmp("put", line_in, 3) == 0) {
			command_put(&line_in[4]);
		}
		rt_thread_mdelay(1000);
	}
#endif
	lwip_close(server_sock);
	return 0;
}

/*
Search through str looking for what. Replace any what with
the contents of by. Do not let the string get longer than max_length.
*/
int replace(char *str, char *what, char *by, int max_length) {
	char *foo, *bar = str;
	int i = 0;
	int str_length, what_length, by_length;

	/* do a sanity check */
	if (! str) return 0;
	if (! what) return 0;
	if (! by) return 0;

	what_length = strlen(what);
	by_length = strlen(by);
	str_length = strlen(str);

	foo = strstr(bar, what);
	/* keep replacing if there is somethign to replace and it
	will no over-flow
	*/
	while ( (foo) && ( (str_length + by_length - what_length) < (max_length - 1) ) ) {
		bar = foo + strlen(by);
		memmove(bar, foo + strlen(what), strlen(foo + strlen(what)) + 1);
		memcpy(foo, by, strlen(by));
		i++;
		foo = strstr(bar, what);
		str_length = strlen(str);
	}
	return i;
}

//send msg to server
void send_msg(char *command, char *msg, int flag) {
	char reply[MAX_INPUT_SIZE + 1];
	if (flag == 0)
		sprintf(reply, "%s\r\n", command);
	else
		sprintf(reply, "%s %s\r\n", command, msg);
	lwip_write(server_sock, reply, strlen(reply));
	rt_kprintf("%d-->%s", strlen(reply), reply);
	return;
}

void help_info() {
	rt_kprintf("?\tcd\tdir\tls\n");
	rt_kprintf("help\tsyst\ttype\tport\n");
	rt_kprintf("pwd\tget\tput\tquit\n");
}

void user_login() {
	rt_kprintf(">Name:");
	//fgets(line_in, MAX_INPUT_SIZE, stdin);
	//line_in[strlen(line_in)-1] = '\0';
	send_msg("USER", "hbq", 1);

	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
	//printf("z = %d, buffer is '%s'\n",z,buffer);
	//if(strncmp("331", buffer, 3) == 0)
	{
		rt_kprintf(">Password:");
		//fgets(line_in, MAX_INPUT_SIZE, stdin);
		//line_in[strlen(line_in)-1] = '\0';
		send_msg("PASS", "123456", 1);

		z = lwip_read(server_sock, buffer, sizeof(buffer));
		buffer[z - 2] = 0;
		rt_kprintf("%s\n", buffer);
		//printf("z = %d, buffer is '%s'\n",z,buffer);
	}
	line_in[0] = 0;
	command_syst();
	return;
}

void command_syst() {
	send_msg("SYST", "", 0);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
}

void command_quit() {
	send_msg("QUIT", "", 0);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
}

void command_type() {
	char msg[2];
	msg[1] = 0;
	if (strncmp("ascii", &line_in[5], 5) == 0) {
		msg[0] = 'A';
		TYPE = TYPE_A;
	} else if (strncmp("binary", &line_in[5], 6) == 0) {
		msg[0] = 'I';
		TYPE = TYPE_I;
	}
	send_msg("TYPE", msg, 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
}

void command_pwd() {
	send_msg("PWD", "", 0);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
}

void command_cd() {
	send_msg("CWD", &line_in[3], 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
}

int command_port() {
#if 0
	//主动模式客户端配置
	if (client_sock < 0) {
		struct sockaddr_in local_addr;
		socklen_t local_addr_len = sizeof local_addr;
		memset(&local_addr, 0, sizeof(local_addr));
		if (lwip_getsockname(server_sock, (struct sockaddr *)&local_addr, &local_addr_len) != 0) {
			rt_kprintf("error when trying to get the local addr\n");
			return -1;
		}
		local_ip = inet_ntoa(local_addr.sin_addr);
		local_port = local_addr.sin_port;
		rt_kprintf("local addr  %s:%d\n", local_ip, local_port);
		char client_port[8] = "";
		sprintf(client_port, "%d.%d", (int)(local_port / 256), (int)(1 + local_port - 256 * (int)(local_port / 256)));
		sprintf(port_addr, "%s.%s", local_ip, client_port);
		replace(port_addr, ".", ",", 24);

		if (data_conn(local_ip, local_port + 1) == -1) return -1;
	}

	send_msg("PORT", port_addr, 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);

	//accept an request
	socklen_t data_addr_len = sizeof data_addr;
	memset(&data_addr, 0, sizeof data_addr);
	data_sock = lwip_accept(client_sock, (struct sockaddr *)&data_addr, &data_addr_len);
	if (data_sock < 0) {
		//close(client_sock);
		rt_kprintf("data_sock accept failed!\n");
		return -1;
	}
	//close(client_sock);
#endif
	//被动模式客户端配置
	send_msg("PASV", "", 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
	char *find;
	//char *ipaddr=NULL;
	int dataPort;
	int a, b, c, d;
	int pa, pb;
	find = strrchr(buffer, '(');
	sscanf(find, "(%d,%d,%d,%d,%d,%d)", &a, &b, &c, &d, &pa, &pb);
	//sprintf(ipaddr, "%d.%d.%d.%d", a, b, c, d);
	dataPort = pa * 256 + pb;
	rt_kprintf("dataPort%d\n", dataPort);
	memset(&data_addr, 0, sizeof(data_addr));
	data_addr.sin_family = AF_INET;
	data_addr.sin_port = htons(dataPort);
	data_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
	rt_memset(&(data_addr.sin_zero), 0, sizeof(data_addr.sin_zero));

	data_sock = lwip_socket(PF_INET, SOCK_STREAM, 0);
	if (data_sock < 0) {
		rt_kprintf(">error on data_sock()\n");
		return (-1);
	}
	if (lwip_connect(data_sock, (struct sockaddr *)&data_addr, sizeof(data_addr)) < 0) {
		rt_kprintf(">error on connect()\n");
		lwip_close(data_sock);
		return (-1);
	}
	//connect to the ftp data_sock successful
	rt_kprintf(">Connected to data_connection %s:%d\n", SERVER_IP, dataPort);

	return 0;
}

int data_conn(char *ip, int port) {
	client_sock = lwip_socket(PF_INET, SOCK_STREAM, 0);
	if (client_sock == -1) {	//create socket failed
		close(client_sock);
		rt_kprintf("data socket() failed");
		return -1;
	}

	//configure server address,port
	memset(&client_addr, 0, sizeof(client_addr));
	socklen_t client_addr_len = sizeof client_addr;
	client_addr.sin_family = AF_INET;
	client_addr.sin_port = htons(port);
	//client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	client_addr.sin_addr.s_addr = inet_addr(ip);
	if (client_addr.sin_addr.s_addr == INADDR_NONE) {
		rt_kprintf("INADDR_NONE");
		return -1;
	}

	//bind
	z = lwip_bind(client_sock, (struct sockaddr *)&client_addr, client_addr_len);
	if (z == -1) {
		lwip_close(client_sock);
		perror("data_sock bind() failed\n");
		return -1;
	}

	//listen
	z = lwip_listen(client_sock, 1);
	if (z < 0) {
		close(client_sock);
		rt_kprintf("data_sock listen() failed!\n");
		return -1;
	}
	rt_kprintf("data conn listening...\n");
	return 0;
}

void command_list() {
	unsigned char databuf[100];
	int bytes = 0, bytesread = 0;

	send_msg("TYPE", "A", 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
	TYPE = TYPE_A;
	rt_kprintf("TYPE%d\n", TYPE);
	if (command_port() == -1) return;
	send_msg("LIST", "", 0);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);
#if 1
	/* Read from the socket and write to stdout */
	while ((bytes = lwip_read(data_sock, databuf, sizeof(databuf)) > 0)) {
		//write(fileno(stdout), databuf, bytes);
		rt_kprintf("%s", databuf);
		bytesread += bytes;
	}

	/* Close the socket */
	lwip_close(data_sock);
#endif
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);

}

void command_get(char *filename) {
	int outfile;
	short file_open = 0;
	unsigned char databuf[FILEBUF_SIZE];
	int bytes = 0, bytesread = 0;

	if (command_port() == -1) return;
	send_msg("RETR", filename, 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);

	/* Read from the socket and write to the file */
	while ((bytes = lwip_read(data_sock, databuf, FILEBUF_SIZE)) > 0) {
		if (file_open == 0) {
			/* Open the file the first time we actually read data */
			if ((outfile = open(filename, O_WRONLY | O_CREAT)) == 0) {
				rt_kprintf("fopen failed to open file");
				lwip_close(data_sock);
				return;
			}
			file_open = 1;
		}
		write(outfile, databuf, bytes);
		bytesread += bytes;
	}

	/* Close the file and socket */
	if (file_open != 0) close(outfile);
	lwip_close(data_sock);

	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);

	rt_kprintf("%d bytes get.\n", bytesread);
}

void command_put(char *filename) {
	int infile;
	unsigned char databuf[FILEBUF_SIZE] = "";
	int bytes = 0, bytessend = 0;

	infile = open(filename, O_RDONLY);
	if (infile == 0) {
		rt_kprintf("fopen() failed");
		//close(data_sock);
		return;
	}

	if (command_port() == -1) return;
	send_msg("STOR", filename, 1);
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);

	while ((bytes = read(infile, databuf, FILEBUF_SIZE)) > 0) {
		//rt_kprintf("read infile\n");
		rt_kprintf("bytes = %d\n", bytes);
		if (TYPE == TYPE_A) {
			//rt_kprintf("lwip_write TYPE_A\n");
			//replace((char *)databuf, "\r\n", "\n", FILEBUF_SIZE-1);
			//replace((char *)databuf, "\n", "\r\n", FILEBUF_SIZE-1);
			lwip_write(data_sock, databuf, sizeof(databuf));
		} else if (TYPE == TYPE_I) {
			rt_kprintf("lwip_write TYPE_I\n");
			lwip_write(data_sock, (const char *)databuf, bytes);
		}

		bytessend += bytes;
		memset(&databuf, 0, FILEBUF_SIZE);
	}
	memset(&databuf, 0, FILEBUF_SIZE);
	rt_kprintf("close(infile)\n");
	close(infile);
	lwip_close(data_sock);
	rt_kprintf("lwip_read server_sock\n");
	z = lwip_read(server_sock, buffer, sizeof(buffer));
	buffer[z - 2] = 0;
	rt_kprintf("%s\n", buffer);

	rt_kprintf("%d bytes send\n", bytessend);
	return;
}

#if 0
int main(void) {
	int count = 1;

	ftpSession(1, 0);
	while (count++) {
		//LOG_D("Hello RT-Thread!");
		rt_thread_mdelay(1000);
	}

	return RT_EOK;
}
#endif

MSH_CMD_EXPORT_ALIAS(ftpthread, ftp, ftp.);






